diff options
Diffstat (limited to 'src/routes/projects/[projectId]/routine/[routineId]')
| -rw-r--r-- | src/routes/projects/[projectId]/routine/[routineId]/+page.server.ts | 21 | ||||
| -rw-r--r-- | src/routes/projects/[projectId]/routine/[routineId]/+page.svelte | 186 |
2 files changed, 125 insertions, 82 deletions
diff --git a/src/routes/projects/[projectId]/routine/[routineId]/+page.server.ts b/src/routes/projects/[projectId]/routine/[routineId]/+page.server.ts index 83bac3c..f2b6551 100644 --- a/src/routes/projects/[projectId]/routine/[routineId]/+page.server.ts +++ b/src/routes/projects/[projectId]/routine/[routineId]/+page.server.ts @@ -31,21 +31,31 @@ export const load: PageServerLoad = async ({ params }) => { } const routine = await loadCurrent(routineId); - if (!routine || routine.archived || routine.projectId !== projectId) { + if (!routine || routine.projectId !== projectId) { throw error(404, 'Routine introuvable'); } const completions = await loadCompletions(routineId); + // An archived routine is frozen at its archive instant: state and chart + // stop there instead of now. + const at = routine.archivedAt ? Date.parse(routine.archivedAt) : Date.now(); return { project: { id: project.id, name: project.name }, routine, completions, - state: computeRoutineState(routine, completions, Date.now()), + state: computeRoutineState(routine, completions, at), }; }; export const actions: Actions = { edit: async ({ request, params }) => { + const current = await loadCurrent(numericId(params.routineId)); + if (!current) { + return fail(400, { error: 'Routine introuvable' }); + } + if (current.archived) { + return fail(400, { error: 'Routine archivée' }); + } const data = await request.formData(); const name = parseFormString(data.get('name')); if (!name) { @@ -65,6 +75,13 @@ export const actions: Actions = { }, complete: async ({ request, params }) => { + const current = await loadCurrent(numericId(params.routineId)); + if (!current) { + return fail(400, { error: 'Routine introuvable' }); + } + if (current.archived) { + return fail(400, { error: 'Routine archivée' }); + } const data = await request.formData(); try { await complete( diff --git a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte index d57ed79..5f57de4 100644 --- a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte +++ b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte @@ -42,8 +42,14 @@ year: 'numeric', }); + const frozenAt = $derived( + data.routine.archived + ? Date.parse(data.routine.archivedAt ?? data.routine.createdAt) + : Date.now(), + ); + const series = $derived( - computeHealthSeries(data.routine, data.completions, Date.now()), + computeHealthSeries(data.routine, data.completions, frozenAt), ); const PAD = 10; @@ -93,65 +99,76 @@ <span class={scoreClass(data.state.health)}>{data.state.health}</span> </header> - <form - class="form-card" - method="POST" - action="?/edit" - use:enhance={() => { - return async ({ result, update }) => { - await update({ reset: false }); - }; - }} - > - {#if form?.error} - <p class="error">{form.error}</p> - {/if} - <label> - <span class="form-label">Nom</span> - <input - class="form-input" - type="text" - name="name" - bind:value={name} - autocomplete="off" - /> - </label> - <label> - <span class="form-label">Intervalle</span> - <input - class="form-input frequency" - class:input-invalid={frequency !== '' && frequencyDays === null} - type="text" - name="frequencyDays" - bind:value={frequency} - placeholder="2w" - autocomplete="off" - aria-label="Intervalle (ex: 1d, 2w, 3m)" - /> - </label> - <label> - <span class="form-label">Description</span> - <textarea - class="form-input desc" - name="description" - bind:value={description} - rows={3} - placeholder="Pourquoi, comment, quand..." - autocomplete="off" - ></textarea> - </label> - {#if frequency !== '' && frequencyDays === null} - <p class="error">Intervalle invalide — ex : 1d, 2w, 3m, 1y</p> - {/if} - <button - class="btn-primary" - type="submit" - disabled={!name.trim() || frequencyDays === null} + {#if data.routine.archived} + <p class="archived-badge"> + Archivée le + {dateTimeFormatter.format( + new Date(data.routine.archivedAt ?? data.routine.createdAt), + )} + </p> + {/if} + + {#if !data.routine.archived} + <form + class="form-card" + method="POST" + action="?/edit" + use:enhance={() => { + return async ({ result, update }) => { + await update({ reset: false }); + }; + }} > - Enregistrer - </button> - <p class="save-status" class:visible={saved}>✓ Enregistré</p> - </form> + {#if form?.error} + <p class="error">{form.error}</p> + {/if} + <label> + <span class="form-label">Nom</span> + <input + class="form-input" + type="text" + name="name" + bind:value={name} + autocomplete="off" + /> + </label> + <label> + <span class="form-label">Intervalle</span> + <input + class="form-input frequency" + class:input-invalid={frequency !== '' && frequencyDays === null} + type="text" + name="frequencyDays" + bind:value={frequency} + placeholder="2w" + autocomplete="off" + aria-label="Intervalle (ex: 1d, 2w, 3m)" + /> + </label> + <label> + <span class="form-label">Description</span> + <textarea + class="form-input desc" + name="description" + bind:value={description} + rows={3} + placeholder="Pourquoi, comment, quand..." + autocomplete="off" + ></textarea> + </label> + {#if frequency !== '' && frequencyDays === null} + <p class="error">Intervalle invalide — ex : 1d, 2w, 3m, 1y</p> + {/if} + <button + class="btn-primary" + type="submit" + disabled={!name.trim() || frequencyDays === null} + > + Enregistrer + </button> + <p class="save-status" class:visible={saved}>✓ Enregistré</p> + </form> + {/if} <h2 class="section-title">Santé</h2> <div class="form-card chart-card"> @@ -197,27 +214,29 @@ </div> <h2 class="section-title">Historique</h2> - <form - class="form-card form-card--compact add-completion" - method="POST" - action="?/complete" - use:enhance={() => { - return async ({ update }) => { - note = ''; - await update({ reset: false }); - }; - }} - > - <input - class="form-input" - type="text" - name="note" - bind:value={note} - placeholder="Note (optionnel)" - autocomplete="off" - /> - <button class="btn-done" type="submit">Fait</button> - </form> + {#if !data.routine.archived} + <form + class="form-card form-card--compact add-completion" + method="POST" + action="?/complete" + use:enhance={() => { + return async ({ update }) => { + note = ''; + await update({ reset: false }); + }; + }} + > + <input + class="form-input" + type="text" + name="note" + bind:value={note} + placeholder="Note (optionnel)" + autocomplete="off" + /> + <button class="btn-done" type="submit">Fait</button> + </form> + {/if} <ul class="item-list"> {#each data.completions as c (c.id)} @@ -305,6 +324,13 @@ border-left: 3px solid var(--accent); } + .archived-badge { + margin: 0 0 1rem; + color: var(--text-secondary); + font-size: 0.875rem; + font-weight: 500; + } + .chart-card { align-items: stretch; } |
